/*
Auhtor      : Pratibh Prakash
Company     : Fujitsu America
Created Date: 14 Sep-2009
Modified    : 23 Sep-2009 updated generation of: Name on Oportunity Forecast record using only Date and excluding time value.
			  Updated populateOpportunityForecastHelper method.
Description : Trigger populates the Opportunity Forecast based on the Product Revenue Schedule.
*/
trigger maintainOpportunityForecastObject on OpportunityLineItem (after insert, before delete, before update) {
	Set<Id> oppLineItemIds = new Set<Id>();
	List<IM_Opportunity_Forecast__c> OppFcstListforDelete = new List<IM_Opportunity_Forecast__c>();
	Map<Id, List<OpportunityLineItemSchedule>> oppProd_ScheduleMap = new Map<Id, List<OpportunityLineItemSchedule>>();
	
	if(Trigger.isAfter && Trigger.isInsert){
		System.debug('***********After Insert Event*********');
		oppLineItemIds.addAll(Trigger.newMap.keySet());
		System.debug('***********Total Opp Prod(INSERTED) Ids:  '+oppLineItemIds.size());
	}
	if(Trigger.isBefore && Trigger.isUpdate){
		System.debug('***********Before Update Event*********');
		for(Integer i=0; i<Trigger.new.size(); i++){
			if(Trigger.new[i].Update_New_Revenue_Forecast__c){//Fileritng records for update when Update_New_Revenue_Forecast__c is checked
				Trigger.new[i].Update_New_Revenue_Forecast__c = false;
				oppLineItemIds.add(Trigger.new[i].Id);
			}
		}
		if(oppLineItemIds.size()>0){
			try{
				//Going to delete Opportunity forecast reocrds in batches as no can be more tahn 1000
				for(List<IM_Opportunity_Forecast__c> OppFcst: [Select Id 
																from IM_Opportunity_Forecast__c 
																where OpportunityLineItemId__c in: oppLineItemIds]){
					if((OppFcstListforDelete.size()+OppFcst.size())<=1000){//making batches of 1000 and then deleting
						OppFcstListforDelete.addAll(OppFcst);
					}	
					else{
						delete OppFcstListforDelete;
						OppFcstListforDelete.clear();
					}									
				}
				if(OppFcstListforDelete.size()>0){
					delete OppFcstListforDelete;
				}
			}
			catch(DMLException dmlex){
				Trigger.new[0].addError('Failed to delete existing Opportunity Forecast records:  '+dmlEx.getDmlId(0)+' Error: '+dmlex.getMessage());
			}
			catch(Exception ex){
				Trigger.new[0].addError('Too many Opportunity Forecast records found 10001. Please reduce the batch size:'+ex.getMessage());
			}
			
		}
		System.debug('***********Total Opp Prod(UPDATED and filtered) Ids:  '+oppLineItemIds.size());
	
	}
	
	if(Trigger.isBefore && Trigger.isDelete){
		System.debug('***********Before Delete Event*********');
		try{
			//Going to delete Opportunity forecast reocrds in batches as no can be more than 1000
			for(List<IM_Opportunity_Forecast__c> OppFcst: [Select Id 
															from IM_Opportunity_Forecast__c 
															where OpportunityLineItemId__c in: Trigger.oldMap.keySet()]){
				if((OppFcstListforDelete.size()+OppFcst.size())<=1000){//making batches of 1000 and then deleting
					OppFcstListforDelete.addAll(OppFcst);
				}	
				else{
					delete OppFcstListforDelete;
					OppFcstListforDelete.clear();
				}									
			}
			if(OppFcstListforDelete.size()>0){
				delete OppFcstListforDelete;
			}
		}
		catch(DMLException dmlex){
			Trigger.new[0].addError('Failed to delete existing Opportunity Forecast records:  '+dmlEx.getDmlId(0)+' Error: '+dmlex.getMessage());
		}
		catch(Exception ex){
			Trigger.new[0].addError('Too many Opportunity Forecast records found 10001. Please reduce the batch size:'+ex.getMessage());
		}
	}
	
	if(oppLineItemIds.size()>0){
		System.debug('***********Starting Process for Inserting Opp FCST Reocrds***');
		
		try{
			for(OpportunityLineItemSchedule olis: [Select Id, OpportunityLineItem.PricebookEntry.Product2.Name,OpportunityLineItem.PricebookEntry.Product2Id,
													OpportunityLineItemId, Revenue, ScheduleDate, OpportunityLineItem.OpportunityId
													from OpportunityLineItemSchedule
													where OpportunityLIneItemId in:oppLineItemIds and Type = 'Revenue'
													order by ScheduleDate Asc]){
				//Creating Map of Opporunity Product and corresponding Opportunity Schedules													
				if(oppProd_ScheduleMap.containsKey(olis.OpportunityLineItemId)){
					oppProd_ScheduleMap.get(olis.OpportunityLineItemId).add(olis);
				}
				else{
					oppProd_ScheduleMap.put(olis.OpportunityLineItemId, new List<OpportunityLineItemSchedule>());
					oppProd_ScheduleMap.get(olis.OpportunityLineItemId).add(olis);
				}
			}
		}
		catch(Exception e){
			Trigger.new[0].addError('Too many Opportunity Schedule records found 10001. Please reduce the batch size:'+e.getMessage());
		}
		
		try{
			List<OpportunityLineItemSchedule> oppProdScheduleList = new List<OpportunityLineItemSchedule>();
			Map<Id, List<OpportunityLineItemSchedule>> tempoppScheduleMap = new Map<Id, List<OpportunityLineItemSchedule>>();
			
			if(oppProd_ScheduleMap.size()>0){
				for(Id oppProdId: oppProd_ScheduleMap.keySet()){
					
					//Making a map of max size size 1000 from Opportunity Product and Opportunity Schedule map for generating Opportunity Forecast records					
					if((oppProdScheduleList.size()+oppProd_ScheduleMap.get(oppProdId).size())<=1000){
						oppProdScheduleList.addAll(oppProd_ScheduleMap.get(oppProdId));
						tempoppScheduleMap.put(oppProdId, new List<OpportunityLineItemSchedule>());
						tempoppScheduleMap.get(oppProdId).addAll(oppProd_ScheduleMap.get(oppProdId));
					}
					//Sending subset Map for processing to generate Opportunity Forecast records
					if((oppProdScheduleList.size()+oppProd_ScheduleMap.get(oppProdId).size())>1000){
						populateOpportunityForecastHelper(tempoppScheduleMap);
						oppProdScheduleList.clear();
						tempoppScheduleMap.clear();
					}
				}
			}
			if(tempoppScheduleMap.size()>0){
				populateOpportunityForecastHelper(tempoppScheduleMap);
			}
		}
		catch(Exception ex){
			Trigger.new[0].addError('Failed to process: '+ex.getMessage()+':'+ex.getCause());
		}
	}
	
	/*helper method: generates Opportunity Foreacst records.
	@param oppProdSchedules: Map of Opportunity Porduct and corresponding schedules.
	*/ 
	private void populateOpportunityForecastHelper(Map<Id, List<OpportunityLineItemSchedule>> oppProdSchedules){
		System.debug('************In Opp Fcst Helper Method********'+oppProdSchedules.size());
		
		List<IM_Opportunity_Forecast__c> tempOppForecastList = new List<IM_Opportunity_Forecast__c>();
		List<OpportunityLineItemSchedule> oppProdScheduleList = new List<OpportunityLineItemSchedule>();
		
		for(Id oppProdId : oppProdSchedules.keySet()){
			oppProdScheduleList.addAll(oppProdSchedules.get(oppProdId));
			System.debug('*********Size for generation******'+oppProdScheduleList.size());
			for(Integer i=0; i<oppProdScheduleList.size(); i++){
				
				OpportunityLineItemSchedule oppProdScheduleCur = oppProdScheduleList[i];
	    		OpportunityLineItemSchedule oppProdSchedulePrev = (i>0 && oppProdScheduleList[i-1].OpportunityLineItemId == oppProdScheduleList[i].OpportunityLineItemId) ? oppProdScheduleList[i-1] : null;
	    		Decimal netNewRevenueThisPeriod = 0;
	    	
	    		if(oppProdSchedulePrev!=null){
	    			netNewRevenueThisPeriod = oppProdScheduleCur.Revenue - oppProdSchedulePrev.Revenue;
	    		} 
	    		else{
	    			netNewRevenueThisPeriod = oppProdScheduleCur.Revenue;
	    		}
	    		//Generating Opportunity forecast record only if difference between revenue of current schedule and previous schedule is not equal to 0
	    		if(netNewRevenueThisPeriod<>0){
	    			IM_Opportunity_Forecast__c oppFcst = new IM_Opportunity_Forecast__c();
					oppFcst.OpportunityLIneItemScheduleId__c = oppProdScheduleCur.Id;
					oppFcst.Name = oppProdScheduleCur.OpportunityLineItem.PricebookEntry.Product2.Name + ' - ' + String.valueOf(oppProdScheduleCur.ScheduleDate);
					oppFcst.Opportunity__c = oppProdScheduleCur.OpportunityLineItem.OpportunityId;
					oppFcst.Product__c = oppProdScheduleCur.OpportunityLineItem.PricebookEntry.Product2Id;
					oppFcst.OpportunityLineItemId__c = oppProdScheduleCur.OpportunityLineItemId;
					oppFcst.Revenue__c = netNewRevenueThisPeriod;
					oppFcst.Schedule_Date__c = oppProdScheduleCur.ScheduleDate;
					tempOppForecastList.add(oppFcst);
				}
			}
			oppProdScheduleList.clear();
		}
		insert tempOppForecastList;
	}
	System.debug('********DML Used********'+Limits.getDMLStatements());
}